#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package}.basic.security.oauth2.server;

import ${package}.basic.security.oauth2.handler.TokenEndpointHandlerInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * Description:
 *    配置类 OAuth2Config, 认证服务器
 * @link {https://projects.spring.io/spring-security-oauth/docs/oauth2.html}
 * @author wupanhua
 * @date 2019/8/6 15:28
 *
 * <pre>
 *              ${copyright}
 *      Copyright (c) 2019. All Rights Reserved.
 * </pre>
 */
@Slf4j
@Configuration
@EnableAuthorizationServer
public class YkAuthOauth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    /**
     * 权限管理器
     */
    private AuthenticationManager authenticationManager;
    /**
     * 登陆逻辑
     */
    private UserDetailsService userDetailsService;
    /**
     * 数据源
     */
    private DataSource dataSource;

    @Resource
    private WebResponseExceptionTranslator<OAuth2Exception> ykoAuthExceptionTranslator;
    @Resource
    private TokenEndpointHandlerInterceptor handlerInterceptorAdapter;

    private AuthorizationServerTokenServices authorizationServerTokenServices;

    /**
     * Configure the security of the Authorization Server, which means in practical terms the /oauth/token endpoint. The
     * /oauth/authorize endpoint also needs to be secure, but that is a normal user-facing endpoint and should be
     * secured the same way as the rest of your UI, so is not covered here. The default settings cover the most common
     * requirements, following recommendations from the OAuth2 spec, so you don't need to do anything here to get a
     * basic server up and running.
     *
     * @param security a fluent configurer for security features
     *
     * @author wenxiaopeng
     * @date 19:24 2020/04/12
     **/
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.allowFormAuthenticationForClients();
        security.checkTokenAccess("permitAll()");
    }

    /**
     * Configure the {@link ClientDetailsService}, e.g. declaring individual clients and their properties. Note that
     * password grant is not enabled (even if some clients are allowed it) unless an {@link AuthenticationManager} is
     * supplied to the {@link ${symbol_pound}configure(AuthorizationServerEndpointsConfigurer)}. At least one client, or a fully
     * formed custom {@link ClientDetailsService} must be declared or the server will not start.
     *
     * @param clients the client details configurer
     * <认证服务器，客户端描述>
     * @author ywh
     * @date 13:43 2019/8/7
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        // 从数据库中读取客户端信息
        clients.jdbc(dataSource);
    }

    /**
     * Configure the non-security features of the Authorization Server endpoints, like token store, token
     * customizations, user approvals and grant types. You shouldn't need to do anything by default, unless you need
     * password grants, in which case you need to provide an {@link AuthenticationManager}.
     *
     * @author wenxiaopeng
     * @date 18:48 2020/04/12
     * @param endpoints 1
     * @return void
     **/
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                .pathMapping("/oauth/confirm_access", "/oauth/custom/confirm_access")
                .exceptionTranslator(ykoAuthExceptionTranslator)
                .addInterceptor(handlerInterceptorAdapter)
            // inject a UserDetailsService, then a refresh token grant will contain a check on the user details,
            // to ensure that the account is still active
            .userDetailsService(userDetailsService)
            .authenticationManager(authenticationManager)
                .tokenServices(authorizationServerTokenServices);
    }

    @Autowired
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Autowired
    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Autowired
    public void setAuthorizationServerTokenServices(AuthorizationServerTokenServices authorizationServerTokenServices) {
        this.authorizationServerTokenServices = authorizationServerTokenServices;
    }
}
